package api

import (
	"fmt"
	"gitee.com/DonHz/electronic-business-systems/mxshop-api/user_web/forms"
	"gitee.com/DonHz/electronic-business-systems/mxshop-api/user_web/global"
	"gitee.com/DonHz/electronic-business-systems/mxshop-api/user_web/global/response"
	"gitee.com/DonHz/electronic-business-systems/mxshop-api/user_web/middlewares"
	"gitee.com/DonHz/electronic-business-systems/mxshop-api/user_web/models"
	"gitee.com/DonHz/electronic-business-systems/mxshop-api/user_web/proto"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func GetUserList(ctx *gin.Context) {
	zap.L().Debug("获取用户列表页...")

	claimsAny, ok := ctx.Get("claims")
	if !ok {
		zap.L().Error("【GetUserList】从上下文获取claims失败")
		return
	}
	claims := claimsAny.(*models.CustomClaims)
	zap.S().Info("访问用户：", claims.UserId)

	pnStr := ctx.DefaultQuery("pn", "0")
	pn, _ := strconv.ParseInt(pnStr, 10, 32)
	pSizeStr := ctx.DefaultQuery("psize", "10")
	pSize, _ := strconv.ParseInt(pSizeStr, 10, 32)
	resp, err := global.UserSrvClient.GetUserList(ctx, &proto.PageInfo{
		Pn:    uint32(pn),
		PSize: uint32(pSize),
	})
	if err != nil {
		zap.L().Error("【GetUserList】查询【用户列表】失败", zap.Error(err))
		HandleGrpcErrorToHttp(err, ctx)
		return
	}
	res := make([]interface{}, 0)
	for _, value := range resp.Data {
		user := response.UserResponse{
			Id:       value.Id,
			UserId:   value.UserId,
			NickName: value.NickName,
			Birthday: time.Unix(value.Birthday, 0).Format("2006-01-02"),
			Gender:   value.Gender,
			Phone:    value.Phone,
		}
		res = append(res, user)
	}
	ctx.JSON(http.StatusOK, res)
}

func LoginPassword(c *gin.Context) {
	var req forms.LoginPassword
	if err := c.ShouldBindJSON(&req); err != nil {
		zap.L().Error("获取请求参数失败", zap.String("method", "LoginPassword"), zap.Error(err))
		HandleValidatorError(c, err)
		return
	}
	if !store.Verify(req.CaptchaId, req.Captcha, false) {
		c.JSON(http.StatusBadRequest, gin.H{
			"msg": "验证码错误",
		})
		return
	}

	if resp, err := global.UserSrvClient.GetUserByPhone(c, &proto.PhoneReq{
		Phone: req.Phone,
	}); err != nil {
		if s, ok := status.FromError(err); ok {
			switch s.Code() {
			case codes.NotFound:
				c.JSON(http.StatusBadRequest, map[string]string{
					"phone": "用户不存在",
				})
			default:
				c.JSON(http.StatusInternalServerError, gin.H{
					"phone": "系统异常，登陆失败",
				})
			}
			return
		}
	} else {
		if res, err := global.UserSrvClient.CheckPassword(c, &proto.CheckPwdInfo{
			Password:          req.Password,
			EncryptedPassword: resp.Password,
		}); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"phone": "手机号或密码错误",
			})
		} else {
			if res.Success {
				// 生成token
				j := middlewares.NewJWT()
				claims := models.CustomClaims{
					Id:       resp.Id,
					UserId:   resp.UserId,
					NickName: resp.NickName,
					Role:     uint8(resp.Role),
					StandardClaims: jwt.StandardClaims{
						NotBefore: time.Now().Unix(), //签名的生效时间
						ExpiresAt: time.Now().Add(time.Second * 24 * 60 * 60 * 30).Unix(),
						Issuer:    "mxshop",
					},
				}
				token, err := j.CreateToken(claims)
				if err != nil {
					c.JSON(http.StatusInternalServerError, gin.H{
						"msg": "生成token失败",
					})
					return
				}
				c.JSON(http.StatusOK, gin.H{
					"id":         resp.Id,
					"user_id":    resp.UserId,
					"nick_name":  resp.NickName,
					"token":      token,
					"expired_at": time.Now().Add(time.Second * 24 * 60 * 60 * 30).Format("2006-01-02 15:04:05"),
					"msg":        "登陆成功",
				})
			} else {
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "登录失败",
				})
				return
			}
		}
	}
}

func Register(ctx *gin.Context) {
	var req forms.RegisterForm
	if err := ctx.ShouldBindJSON(&req); err != nil {
		zap.L().Error("获取请求参数失败", zap.String("method", "Register"), zap.Error(err))
		HandleGrpcErrorToHttp(err, ctx)
		return
	}
	// 两次输入密码校验
	if req.Password != req.RePassword {
		ctx.JSON(http.StatusBadRequest, gin.H{"msg": "两次输入密码不一致"})
	}
	// 验证码校验
	rdb := redis.NewClient(&redis.Options{
		Addr: fmt.Sprintf("%s:%d", global.ServerConfig.RedisInfo.Host, global.ServerConfig.RedisInfo.Port),
	})
	val, err := rdb.Get(ctx, req.Phone).Result()
	if err == redis.Nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"msg": "验证码错误",
		})
		return
	} else {
		if val != req.Code {
			ctx.JSON(http.StatusBadRequest, gin.H{
				"msg": "验证码错误",
			})
			return
		}
	}
	user, err := global.UserSrvClient.CreateUser(ctx, &proto.CreateUserInfo{
		NickName: req.Phone,
		Password: req.Password,
		Phone:    req.Phone,
	})
	if err != nil {
		zap.L().Error("【Register】注册用户失败", zap.Error(err))
		HandleGrpcErrorToHttp(err, ctx)
		return
	}
	j := middlewares.NewJWT()
	claims := models.CustomClaims{
		Id:       user.Id,
		UserId:   user.UserId,
		NickName: user.NickName,
		Role:     uint8(user.Role),
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix(), //签名的生效时间
			ExpiresAt: time.Now().Add(time.Second * 24 * 60 * 60 * 30).Unix(),
			Issuer:    "mxshop",
		},
	}
	token, err := j.CreateToken(claims)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "生成token失败",
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"id":         user.Id,
		"user_id":    user.UserId,
		"nick_name":  user.NickName,
		"token":      token,
		"expired_at": time.Now().Add(time.Second * 24 * 60 * 60 * 30).Format("2006-01-02 15:04:05"),
		"msg":        "登陆成功",
	})
}

func HandleGrpcErrorToHttp(err error, c *gin.Context) {
	if err != nil {
		if s, ok := status.FromError(err); ok {
			switch s.Code() {
			case codes.NotFound:
				c.JSON(http.StatusNotFound, gin.H{
					"msg": s.Message(),
				})
			case codes.Internal:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "内部错误",
				})
			case codes.InvalidArgument:
				c.JSON(http.StatusBadRequest, gin.H{
					"msg": "参数错误",
				})
			case codes.Unavailable:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "用户服务不可用",
				})
			case codes.AlreadyExists:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "用户已注册",
				})
			default:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "未知错误",
				})
			}
		}
	}
	return
}

func removeTopStruct(fileds map[string]string) map[string]string {
	resp := map[string]string{}
	for field, err := range fileds {
		resp[field[strings.Index(field, ".")+1:]] = err
	}
	return resp
}

func HandleValidatorError(c *gin.Context, err error) {
	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		c.JSON(http.StatusOK, gin.H{
			"msg": err.Error(),
		})
	}
	c.JSON(http.StatusBadRequest, gin.H{
		"error": removeTopStruct(errs.Translate(global.Trans)),
	})
}
